package org.arl.modem.linktuner;

import jade.core.AID;

import java.util.*;
import java.util.logging.Level;

import org.arl.modem.FAPIAgent;
import org.arl.modem.FAPIMessage;
import org.arl.modem.link.Link;
import org.arl.modem.phy.Physical;

/// @brief Simple linkTuner implementation.
///
/// This agent implements a very simple link tuner, which actually 
/// does no tuning.
///
/// @author Shankar
/// @version $Id: linkTuner.java Shankar $
///
/// @see For list of supported properties, see @ref prop
/// @see For list of supported requests & notifications, see @ref msg

@SuppressWarnings("serial")
public class linkTuner extends FAPIAgent{
	/// Transmission queue clear request.	
	protected  AID link;
	protected  AID phy;
	protected  AID notify;
	protected static int ACTIVE_SCHEME=2;

	private static int errorCnt=0;
	private static int rxTestPktCnt=0;
	private static int testPktCnt=0;
	private static int testPktTxAttemptCnt=0;
	private static int BEGIN_PKT_TRAIN_FLAG=0;
	
	private static boolean testPktTrainTxInProgress=false;
	private static int testPktTrainCnt=0;

	private static int rxTestPktCntPrev=0;
	private static int errorCntPrev=0;
	private static int testResultsAckCnt=0;

	public final static int TYPE_ACTIVATE_SCHEME=0xFF;
	public final static int TYPE_SET_SCHEME_PARAM_M=0xFE;
	public final static int TYPE_SEND_SCHEME_PARAM_ACK_S=0xFD;
	
	public final static int TYPE_QUERY_TEST_RESULTS_M=0xFC;
	public final static int TYPE_TEST_RESULT_QUERY_ACK_S=0XFB;
	
	public final static int TYPE_SET_SCHEME_PARAM_M_headerLen=2;
	//IOFDM param list
	protected final static int Nc=256;
	protected final static int Np=257;
	protected final static int Ns=258;
	protected final static int Nz=259;
	protected final static int TESTPKT=15;
	protected final static int PKT_DURATION=14;

	protected final static int Scheme_Params[]={Nc,Np,Ns,Nz};
	protected final static int Scheme_Params_Values[][]={ {256,512},
														{0,8,16,32,64,128,256,256},
														{5},
														{0}
														};

	public final static List<Integer[]> schemeParamSuperSet=genValidSchemeParamSuperSet(Scheme_Params, Scheme_Params_Values);
	
	public static int maxTestPktTrainCnt=schemeParamSuperSet.size();
	public static int[] currentSetSchemeParam=newSetSchemeParam(testResultsAckCnt,schemeParamSuperSet); //example: currentSetSchemePara(2,3,0,2) indicates Nc=128, Np=128, Ns=5,Nz=64....
	
	private static int[] rxTestPktCntContainer=new int[maxTestPktTrainCnt];
	private static int[] errorCntContainer=new int[maxTestPktTrainCnt];
	
	public final static int LINK_TUNER_PROTOCOL_ID=3;
	
	public final static String NOTIFICATIONS = "linkTuner-ntf";
	
	protected stopWatch sw = new stopWatch();
	//protected protocol ltProtocol;

	public void setup() {
		super.setup();
		info("$Id: linkTuner agent $");
		subscribeTopic(Link.NOTIFICATIONS);
		subscribeTopic(Physical.NOTIFICATIONS);
		link = new AID(getAgentProperty("lnk", "lnk"), AID.ISLOCALNAME);
	    phy = new AID(getAgentProperty("phy", "phy"), AID.ISLOCALNAME);
		notify = getTopic(NOTIFICATIONS);
		//ltProtocol = new protocol();
		System.out.println("link tuning agent is loaded");
	}
	
	protected void handleMessage(FAPIMessage msg) {	
		if (msg.getPerformative() == FAPIMessage.REQUEST) {
			if(msg.getContent()==PARAM_SET){
				log.fine("msg content="+msg.getContent());
			}
			FAPIMessage rsp = msg.createReply();
			rsp.setPerformative(FAPIMessage.AGREE);
			send(rsp);
		}
		if(msg.getSender().equals(phy)){
			log.fine("recieved a phy layer notification "+ msg.get("type"));
			if(msg.get("type")=="RX.TESTPKT.CNT"){
				rxTestPktCnt++;
				int eCnt=msg.getAsInteger("errors", 0);
				log.fine("received a testpacket! error count = "+ eCnt);
				errorCnt=errorCnt+eCnt;
				log.fine("Current error count = " + errorCnt);
				log.fine(" rxTestPktCnt= " + rxTestPktCnt);
			}
			if(msg.get("type")=="TX.PKT.TIME"){
				sendTestPacketTrain(ACTIVE_SCHEME, linkTunerCommands.batchSize);
			}
		}		
		FAPIMessage ntf=createMessage(FAPIMessage.INFORM,notify);
		switch (msg.getAsInteger("linkTuningCommands",-1)){
			case linkTunerCommands.INIT_LT_COMMAND:
				activateTestPktErrorCnt();
				break;
			case linkTunerCommands.E_RESET_COMMAND:
				reset();
				break;
			case linkTunerCommands.BEGIN_LT_COMMAND:
				int recommendedScheme;
				recommendedScheme=recommendScheme();
				setSchemeRequest(recommendedScheme, currentSetSchemeParam);
				break;
			case linkTunerCommands.E_LT_COMMAND:
				sendTestPacketTrain(ACTIVE_SCHEME,linkTunerCommands.batchSize, true);  //remember to pass the third argument as true
				break;
			case TYPE_SET_SCHEME_PARAM_M:
				setSchemeParam(msg, 1); //call with second argument=1 if you want to return an ack
				break;
			case TYPE_ACTIVATE_SCHEME:
				activateScheme(msg);
				break;
			case TYPE_SEND_SCHEME_PARAM_ACK_S:
				processSendSchemeParamAck(msg);
				break;
			case TYPE_QUERY_TEST_RESULTS_M:
				processTestResultQuery(msg);
				break;
			case TYPE_TEST_RESULT_QUERY_ACK_S:
				log.fine("about to call processTestResultsAck()");
				processTestResultsAck(msg);
				break;
		}
		send(ntf);
	}
	
	//Master sends out a request to the slave to set some scheme params 
	private boolean setSchemeRequest(int recommendedScheme, int[] currentSetSchemeParam){
		log.fine("in setShemeRequest() method");
		byte[] setSchemeParam=new byte[currentSetSchemeParam.length];
		for(int i=0;i<currentSetSchemeParam.length;i++){
			setSchemeParam[i]=(byte)currentSetSchemeParam[i];
		}
		FAPIMessage req=createMessage(FAPIMessage.REQUEST,link);
		req.add("protocol",LINK_TUNER_PROTOCOL_ID);
		byte[] rScheme=new byte[(TYPE_SET_SCHEME_PARAM_M_headerLen+setSchemeParam.length)];
		rScheme[0]=(byte)TYPE_SET_SCHEME_PARAM_M;
		rScheme[1]=(byte)recommendedScheme;
		System.arraycopy(setSchemeParam, 0, rScheme, 2, setSchemeParam.length);
		req.setData(rScheme);
		log.fine("req.getData().length = "+req.getData().length);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("master has sent a request to set scheme parameters:"+rScheme[2]+"  "+rScheme[3]+"  "+rScheme[4]+"  "+rScheme[5]);
			return true;
		}
		else {
			log.warning("not successful in sending out set scheme parameter request");
			diagnose(reply);
			return false;
		}
	}
	//Sets ownself's physical layer params. Use value==1 upon receiving the setSchemeRequest to send back an ack. Use any other value
	//if you are the master and have received an ack, and just want to set your own params.   
	private void setSchemeParam(FAPIMessage msg, int value){
			//byte[] valueArray=new byte[msg.getData().length-TYPE_SET_SCHEME_PARAM_M_headerLen];
			log.fine("msg.getData().length = "+ msg.getData().length);
			byte[] valueArrayReceived=new byte[currentSetSchemeParam.length];
			log.fine("size of valueArrayReceived = "+valueArrayReceived.length);
			System.arraycopy(msg.getData(), TYPE_SET_SCHEME_PARAM_M_headerLen, valueArrayReceived, 0, valueArrayReceived.length);
			boolean setSchemeParamOperation=true;
			int n=0;
			while(n<valueArrayReceived.length){
				FAPIMessage reply=setSingleSchemeParam(Scheme_Params[n], Scheme_Params_Values[n][valueArrayReceived[n]], ACTIVE_SCHEME);
				if(reply!=null && reply.getPerformative()==FAPIMessage.INFORM){
					log.fine("set ownself's parameter "+reply.getAsInteger("parameter", -1)+" to value = "+reply.getAsInteger("value", -1));
				}
				else{
					setSchemeParamOperation=false;
					log.warning("failed to set scheme paramter");
				}
				n++;
			}
		
		log.fine("just asked the phy layer to set params");
		if (setSchemeParamOperation==false){
	    	log.warning("----seem unable to set phy params :(");
	    }
		else{
	    	if(value==1){
	    		log.fine("slave is now going to return ack");
	    		sendSchemeParamAck(msg); //send back an ack iff value==1
	    	}
	    	else if(value==0){
	    		log.fine("proceeding to send test packet train");
	    		sendTestPacketTrain(ACTIVE_SCHEME,linkTunerCommands.batchSize, true);
	    	}
	    	
		}
	}
	//sets the value for a single parameter
	private FAPIMessage setSingleSchemeParam(int param, int value, int family){
		log.fine("inside the setSingleSchemeParam method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy, Physical.PARAM_SET);
		req.setContent(PARAM_SET);
		req.add("family", family);
		req.add("parameter", param);
		req.add("value",value);
		FAPIMessage reply=request(req,2000);
		return reply;
	}
	private void sendSchemeParamAck(FAPIMessage msg){
		log.fine("inside the sendSchemeParamAck method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST,link);
		req.add("protocol",LINK_TUNER_PROTOCOL_ID);
		byte[] rScheme=msg.getData();
		rScheme[0]=(byte)TYPE_SEND_SCHEME_PARAM_ACK_S;
		req.setData(rScheme);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("slave has sent an ack back to the master");
			//return true;
		}
		else {
			log.warning("slave is not successful in sending out set scheme parameter ack");
			diagnose(reply);
			//return false;
		}
	}
	
	private void processSendSchemeParamAck(FAPIMessage msg){
		byte[] valueArray=new byte[currentSetSchemeParam.length];
		System.arraycopy(msg.getData(), TYPE_SET_SCHEME_PARAM_M_headerLen, valueArray, 0, valueArray.length);
		byte[] currentSetSchemeParamByteArray=new byte[currentSetSchemeParam.length];
		for(int i=0;i<currentSetSchemeParam.length;i++){
			currentSetSchemeParamByteArray[i]=(byte)currentSetSchemeParam[i];
		}
		if(Arrays.equals(currentSetSchemeParamByteArray, valueArray)){
			log.fine("received ack corresponding to current set scheme param request");
			setSchemeParam(msg, 0);  //remember to pass the second argument as zero if you are the master and want to send a test pkt train after setting your params. 
		}
		else{
			log.fine("recieved ack does not match current set scheme param request");
		}
	}
	
 	private int recommendScheme(){
		return 2;
	}
	
	private void activateScheme(FAPIMessage msg){
		log.fine("inside the activateScheme() method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy, Physical.PARAM_SET);
		req.setContent(PARAM_SET);
		req.add("family", msg.getData()[1]);
		req.add("parameter", 1);
		req.add("value",1);
		FAPIMessage reply=request(req, 2000);
		if (reply == null || reply.getPerformative() != FAPIMessage.INFORM || deactivateScheme(msg)){
			log.warning("unable to activate scheme" + msg.getData()[1]);
		}
		else {
			ACTIVE_SCHEME=msg.getData()[1];
			log.fine("-----currently, ACTIVE_SCHEME ="+ACTIVE_SCHEME);
			log.fine("successfully activated scheme " + msg.getData()[1]);
			
		}

	}
	
	private boolean deactivateScheme(FAPIMessage msg){
		log.fine("inside the deactivateScheme() method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy, Physical.PARAM_SET);
		req.setContent(PARAM_SET);
		req.add("family", msg.getData()[1]);
		req.add("parameter", 1);
		req.add("value",0);
		FAPIMessage reply=request(req, 2000);
		if (reply == null || reply.getPerformative() != FAPIMessage.INFORM){
	    	log.warning("----seem unable to deactivate scheme " + msg.getData()[1]);
	    	return false;
	    }
		else{
	    	log.fine(" successfully deactivated scheme " + msg.getData()[1]);
	    	return true;
		}
	}
	
	
	private boolean activateTestPktErrorCnt(){
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy, Physical.PARAM_SET);
		req.setContent(PARAM_SET);
		req.add("family", ACTIVE_SCHEME);
		req.add("parameter", TESTPKT);
		req.add("value",1);
		FAPIMessage reply=request(req, 2000);
		if (reply == null || reply.getPerformative() != FAPIMessage.INFORM){
	    	log.warning("----seem unable to activate test pkt error count :(");
	    	return false;
	    }
		else{
	    	log.fine(" parameter TESTPKT (" + reply.getAsInteger("parameter", -1) + ") has value= " + reply.getAsInteger("value",-1) + " for scheme " + reply.getAsInteger("family"));
	    	return true;
		}
	}
	
	//returns the elapsed time at the end of transmitting test packet train, else returns -1 
	private void sendTestPacketTrain(int activeScheme, int batchSize, boolean beginPktTrainFlag){
		if(beginPktTrainFlag){
			BEGIN_PKT_TRAIN_FLAG=1;
			testPktTrainTxInProgress=true;
			log.fine("set BEGIN_PKT_TRAIN_FLAG.");
			sw.start();
			sendTestPacketTrain(activeScheme, batchSize);
		}
		else{
			log.fine("failed to set BEGIN_PKT_TRAIN_FLAG.");
		}		
	}
	private void sendTestPacketTrain(int activeScheme, int batchSize){
		if(BEGIN_PKT_TRAIN_FLAG==1 && testPktTxAttemptCnt<batchSize){
			if(sendTestPacket(activeScheme)){
				testPktCnt++;
				log.fine("testPktCnt = "+testPktCnt +" , testPktTxAttemptCnt = "+(testPktTxAttemptCnt+1));
			}
			else{
				log.fine("failed to transmit test pkt");
			}
			testPktTxAttemptCnt++;
		}
		if(testPktTxAttemptCnt>=batchSize){
			sw.stop();
	        log.fine("elapsed time in milliseconds: " + sw.getElapsedTime());
			testPktTxAttemptCnt=0;
			BEGIN_PKT_TRAIN_FLAG=0;
			testPktTrainTxInProgress=false;
			testPktTrainCnt++;      //to indicate that we have finished transmitting a train of test packets.
			testSchemeParams();
			log.fine("done transmitting test pkt train.");
		}
	}
	
	//while trying to send out a test packet, do remember to add dummy/0-byte data and scheme, even though the docu-
	//mentation says there is no need for it. For some reference see PhysicalCommands.TX_ The TX_ method is not
	//called explicitly, rather it is invoked by reflection (ie dynamically when u type in the at commands).
	private boolean sendTestPacket(int activeScheme){
		log.fine("inside the sendTestPackets() method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy);
		req.setData(new byte[0]);
		req.add("type", "TESTPKT");
		req.add("scheme", activeScheme);
	    req.add("lowpri", 0);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("master has sent out a test packet");
			return true;
		}
		else {
			log.fine("not successful in sending out test packets");
			//ltProtocol.diagnose(reply);
			return false;
		}
	}
	
	//Gets called whenever the train of test packet transmissions finishes.
	private void testSchemeParams(){
		if(!testPktTrainTxInProgress && testPktTrainCnt<maxTestPktTrainCnt){
			//do some condition checking here
			if(testPktTrainCnt>0){
				queryTestResults(testPktTrainCnt);		
				return;
			}
			//sendTestPacketTrain(ACTIVE_SCHEME,linkTunerCommands.batchSize, true);
		}
	}
		
	private boolean queryTestResults(int testPktTrainCnt){
		FAPIMessage req=createMessage(FAPIMessage.REQUEST,link);
		req.add("protocol",LINK_TUNER_PROTOCOL_ID);
		//req.add("scheme",ACTIVE_SCHEME); 
		byte[] rQuery=new byte[2];
		rQuery[0]=(byte)TYPE_QUERY_TEST_RESULTS_M; //remember to try encoding the testPktTrainCnt somewhere in there....
		rQuery[1]=(byte)(testPktTrainCnt&0x000000FF);
		req.setData(rQuery);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("master has queried for test results");
			return true;
		}
		else {
			log.warning("not successful in sending out test result query");
			diagnose(reply);
			return false;
		}
	}
	
	//returns an ack to the master with the errorCnt and the rxTestPktCnt
	//Packet format:
	//byte 0:TYPE_TEST_RESULT_QUERY_ACK_S, byte1:identifier data, bytes2-5: rxTestPktCnt, bytes6-9:errorCnt.
	private void processTestResultQuery(FAPIMessage msg){
		log.fine("inside the processTestResultQuery method");
		FAPIMessage req=createMessage(FAPIMessage.REQUEST,link);
		req.add("protocol",LINK_TUNER_PROTOCOL_ID);
		byte[] rRsp=new byte[10];
		rRsp[0]=(byte)TYPE_TEST_RESULT_QUERY_ACK_S;
		rRsp[1]=msg.getData()[1]; //contains an identifier to ensure that the ack corresponds to the correct query
		byte[] v1 = intToByteArray(rxTestPktCnt);
		byte[] v2 = intToByteArray(errorCnt);
		System.arraycopy(v1,0,rRsp,2,4);
		System.arraycopy(v2,0,rRsp,6,4);
		req.setData(rRsp);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("slave has processed the test result query and sent back an ack");
			//return true;
		}
		else {
			log.warning("slave is not successful in processing test result query");
			diagnose(reply);
			//return false;
		}
	}
	
	private void processTestResultsAck(FAPIMessage msg){
		log.fine("inside the processTestResultsAck method");
		//check if ack and query match
		if(((byte)(testPktTrainCnt&0x000000FF))==msg.getData()[1]){
			log.fine("test result ack matches the latest query");
			byte[] v1=new byte[4];
			byte[] v2=new byte[4];
			System.arraycopy(msg.getData(), 2, v1, 0, 4);
			System.arraycopy(msg.getData(), 6, v2, 0, 4);
			int t1=byteArrayToInt(v1);
			int t2=byteArrayToInt(v2);	
			//remember that testPktTrainCnt starts from 1, so u need to subtract 1 from it so that the container index starts from zero.
			if(testPktTrainCnt<rxTestPktCntContainer.length && testPktTrainCnt>0){
				if(testPktTrainCnt==1){
					rxTestPktCntContainer[testPktTrainCnt-1]=t1;
					errorCntContainer[testPktTrainCnt-1]=t2;
				}
				else{
					rxTestPktCntContainer[testPktTrainCnt-1]=t1-rxTestPktCntPrev;
					errorCntContainer[testPktTrainCnt-1]=t2-errorCntPrev;
				}
				rxTestPktCntPrev=t1;
				errorCntPrev=t2;
				testResultsAckCnt++;
				log.fine("rxTestPktCnt = "+ t1 + " errorCnt = " + t2);
				log.fine("rxTestPktCntContainer["+(testPktTrainCnt-1)+"]"+"="+rxTestPktCntContainer[testPktTrainCnt-1]+"  "+"errorCntContainer["+(testPktTrainCnt-1)+"]"+"="+errorCntContainer[testPktTrainCnt-1]);
				log.fine("currentSetSchemeParam = "+currentSetSchemeParam[0]+" "+currentSetSchemeParam[1]+" "+currentSetSchemeParam[2]+" "+currentSetSchemeParam[3]);
				//Note that for BER calculation, the packet size is hard-coded to be 9.
				log.fine("Nc= "+Scheme_Params_Values[0][currentSetSchemeParam[0]]+", Np= "+Scheme_Params_Values[1][currentSetSchemeParam[1]]+ ", BER= "+(errorCntContainer[testPktTrainCnt-1]/(rxTestPktCntContainer[testPktTrainCnt-1])*9*8));
				log.fine("Nc, Np, rxTestPktCntContainer, errorCntContainer = "+Scheme_Params_Values[0][currentSetSchemeParam[0]]+" "+Scheme_Params_Values[1][currentSetSchemeParam[1]]+" "
				                                                               +rxTestPktCntContainer[testPktTrainCnt-1]+" "+errorCntContainer[testPktTrainCnt-1]);
				if(linkTunerCommands.enableCycleTest>0){
					log.fine("initiating next scheme param change");
					if((currentSetSchemeParam=newSetSchemeParam(testResultsAckCnt, schemeParamSuperSet))!=null){
						log.fine("about to call setSchemeRequest()");
						setSchemeRequest(ACTIVE_SCHEME, currentSetSchemeParam);
					}
					else{
						log.warning("index out of bounds");
					}
					return;	
				}else if(linkTunerCommands.enableCycleTest==0){
					log.fine("proceeding to send test packet train");
		    		sendTestPacketTrain(ACTIVE_SCHEME,linkTunerCommands.batchSize, true);
				}
			}
			else{
				log.warning("container index out of bounds");
				return;
			}
		}
	}
	
	//performs validity checks to ensure that the param-value pairs are valid 
	private static List<Integer[]> genValidSchemeParamSuperSet(int[] Scheme_Params, int[][] Scheme_Params_Values){
		List<Integer[]> schemeParamSuperSetTemp=genSchemeParamSuperSet(Scheme_Params, Scheme_Params_Values);
		List<Integer[]> schemeParamSuperSetRefined=new ArrayList<Integer[]>();
		//checks that Nz<Nc.
		for(int i=0;i<schemeParamSuperSetTemp.size();i++){
			if(Scheme_Params_Values[3][schemeParamSuperSetTemp.get(i)[3].intValue()]<Scheme_Params_Values[0][schemeParamSuperSetTemp.get(i)[0].intValue()]){
//				System.out.println("vector under consideration = "+schemeParamSuperSet.get(i)[0]+" "+schemeParamSuperSet.get(i)[1]+" "+schemeParamSuperSet.get(i)[2]+" "+schemeParamSuperSet.get(i)[3]);
//				System.out.println("about to remove: "+"i = "+i+" Nz = "+Scheme_Params_Values[3][schemeParamSuperSet.get(i)[3].intValue()]+", Nc = "+Scheme_Params_Values[0][schemeParamSuperSet.get(i)[0].intValue()]);
//				System.out.println("wtf: "+"Scheme_Params_Values[3][schemeParamSuperSet.get(i)[3].intValue()] = "+Scheme_Params_Values[3][schemeParamSuperSet.get(i)[3].intValue()]+", Scheme_Params_Values[3][schemeParamSuperSet.get(i)[3].intValue()] = "+Scheme_Params_Values[0][schemeParamSuperSet.get(i)[0].intValue()]);
				schemeParamSuperSetRefined.add(schemeParamSuperSetTemp.get(i));
			}
		}
		return schemeParamSuperSetRefined;
	}
	
	//function to generate all possible combos, based on Scheme_Params, Scheme_Params_Values, and the test result query count
	private static List<Integer[]> genSchemeParamSuperSet(int[] Scheme_Params, int[][] Scheme_Params_Values){
		List<Integer[]> schemeParamSuperSet=new ArrayList<Integer[]>();
		Integer[] schemeParamIndex=new Integer[Scheme_Params.length];
		int i0=0;
		while(i0<Scheme_Params_Values[0].length){
			schemeParamIndex[0]=i0;
			i0++;
			int i1=0;
			while(i1<Scheme_Params_Values[1].length){
				schemeParamIndex[1]=i1;
				i1++;
				int i2=0;
				while(i2<Scheme_Params_Values[2].length){
					schemeParamIndex[2]=i2;
					i2++;
					int i3=0;
					while(i3<Scheme_Params_Values[3].length){
						schemeParamIndex[3]=i3;
						i3++;
						schemeParamSuperSet.add(schemeParamIndex.clone());						
					}
				}
			}
		}
		return schemeParamSuperSet;
	}
	
	private static int[] newSetSchemeParam(int testResultsAckCnt, List<Integer[]> schemeParamSuperSet){
		System.out.println("inside the newSetSchemeParam() method, test resultsAckCnt = "+testResultsAckCnt);
		if(testResultsAckCnt>=0 && testResultsAckCnt<schemeParamSuperSet.size()){
			int[] setSchemeParam=new int[schemeParamSuperSet.get(testResultsAckCnt).length];
			for(int i=0;i<setSchemeParam.length;i++){
				setSchemeParam[i]=schemeParamSuperSet.get(testResultsAckCnt)[i].intValue();
			}
			System.out.println("retreived vector = "+setSchemeParam[0]+" "+setSchemeParam[1]+" "+setSchemeParam[2]+" "+setSchemeParam[3]);
			return setSchemeParam;
		}
		else{
			return null;
		}
	}
	
	private void reset(){
		log.fine("proceeding to reset linkTuner");
		ACTIVE_SCHEME=2;
		
		errorCnt=0;
		rxTestPktCnt=0;
		testPktCnt=0;
		testPktTxAttemptCnt=0;
		BEGIN_PKT_TRAIN_FLAG=0;
		
		testPktTrainTxInProgress=false;
		testPktTrainCnt=0;

		rxTestPktCntPrev=0;
		errorCntPrev=0;
		testResultsAckCnt=0;
		
		currentSetSchemeParam=newSetSchemeParam(testResultsAckCnt,schemeParamSuperSet); //example: currentSetSchemePara(2,3,0,2) indicates Nc=128, Np=128, Ns=5,Nz=64....
		
		rxTestPktCntContainer=new int[maxTestPktTrainCnt];
		errorCntContainer=new int[maxTestPktTrainCnt];
	}
	//helper functions	
	public static final byte[] intToByteArray(int value) {
        return new byte[] {
                (byte)(value >>> 24),
                (byte)(value >>> 16),
                (byte)(value >>> 8),
                (byte)value};
	}
	public static final int byteArrayToInt(byte [] b) {
        return (b[0] << 24)
                + ((b[1] & 0xFF) << 16)
                + ((b[2] & 0xFF) << 8)
                + (b[3] & 0xFF);
	}
	//can get the packet duration of the active scheme
	private int getPktDuration(int activeScheme){
		FAPIMessage req=createMessage(FAPIMessage.REQUEST, phy, Physical.PARAM_SET);
		req.setContent(PARAM_GET);
		req.add("family", activeScheme);
		req.add("parameter",PKT_DURATION);
		FAPIMessage reply=request(req, 2000);
		if (reply == null || reply.getPerformative() != FAPIMessage.INFORM){
			log.warning("----seem unable to retreive packet duration");
			//ltProtocol.diagnose(reply);
			return -1;
		}
		else{
			log.fine("packet duration for scheme " +activeScheme+" = "+reply.getAsInteger("value", -1));
			return reply.getAsInteger("value", -1);
		}
	}
	private void diagnose(FAPIMessage reply){
		if(reply==null)log.fine("reply==null");
		if(reply.getPerformative()==FAPIMessage.FAILURE)log.fine("perf=failiure");
		if(reply.getPerformative()==FAPIMessage.NOT_UNDERSTOOD)log.fine("perf=not-understood");
		if(reply.getPerformative()==FAPIMessage.REFUSE)log.fine("perf=refuse");
		if(reply.getPerformative()==FAPIMessage.DISCONFIRM)log.fine("perf=disconfirm");
		log.fine("exiting diagnose");
	}
	
	public void stackDump(Throwable ex) {
		  log.log(Level.WARNING, "LinkTuner Stack Dump:", ex);
		  log.fine("LinkTuner Stack Dump:");
		  ex.printStackTrace();
		}
	
	//call this if you just want to activate a scheme.
	private boolean setSchemeRequest(int recommendedScheme){
		FAPIMessage req=createMessage(FAPIMessage.REQUEST,link);
		req.add("protocol", LINK_TUNER_PROTOCOL_ID);
		byte[] rScheme=new byte[2];
		rScheme[0]=(byte)TYPE_ACTIVATE_SCHEME;
		rScheme[1]=(byte)recommendedScheme;
		req.setData(rScheme);
		FAPIMessage reply=request(req, 2000);
		if(reply != null && reply.getPerformative() == FAPIMessage.AGREE){
			log.fine("master has sent a request to activate scheme"+rScheme[1]);
			return true;
		}
		else {
			log.fine("not successful in sending out set scheme request");
			return false;
		}
	}

}

